Skip to content

Isolate browser WebKit process pools#4987

Merged
lawrencecchen merged 16 commits into
mainfrom
task-webview-crash-isolation
Jun 2, 2026
Merged

Isolate browser WebKit process pools#4987
lawrencecchen merged 16 commits into
mainfrom
task-webview-crash-isolation

Conversation

@lawrencecchen
Copy link
Copy Markdown
Contributor

@lawrencecchen lawrencecchen commented May 29, 2026

Summary

  • give each browser panel its own WKProcessPool so one bad page is less likely to take sibling browser panels down with it
  • keep popups in the opener process pool and close popup windows when their WebContent process terminates
  • add regression coverage for process-pool isolation, WebView replacement, and popup termination cleanup

Testing

  • ./scripts/reload.sh --tag webcrash
  • ./scripts/reload.sh --tag webcrash --launch
  • Loaded a local hostile page at http://127.0.0.1:18787/?cap=4096; it pushed the WebContent process to roughly 2.1 GiB RSS while cmux stayed around 430 MiB RSS.
  • Sent SIGKILL to the probe WebContent PID; cmux stayed alive, switching to a terminal tab worked, and echo cmux survived WebContent SIGKILL ran successfully.
  • Targeted AWS tests were not run because cmux-aws-m4pro had only 363 MiB free on / after cleanup.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Touches core WebKit lifecycle, navigation restore, and popup teardown; behavior change from shared pools may affect cookie/process assumptions across tabs while data stores stay shared.

Overview
Each browser panel now gets its own WKProcessPool instead of sharing one app-wide pool, so a WebContent crash is meant to stay confined to that tab while profile WKWebsiteDataStore sharing is unchanged. Popups still use the opener’s pool via BrowserPopupBrowserContext and are closed when their WebContent process terminates.

After a content-process death, the panel rebuilds WKWebView on the same pool but does not auto-reload; it sets recoverable state, picks a smarter restore URL (including in-flight navigations), and shows a Reload overlay. Toolbar/menu reload and recoverTerminatedWebContent perform the actual navigation; profile switch, workspace reset, and normal navigation clear pending recovery.

configureWebViewConfiguration now takes an optional process pool (omit to preserve an existing pool). New BrowserWebContentProcessTests and a Khmer Reload string were added.

Reviewed by Cursor Bugbot for commit 652a87a. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Isolated each browser panel into its own WKProcessPool so crashes only affect that tab, and added a manual recovery flow with a Reload overlay; toolbar/menu Reload attempts recovery first. Popups stay in the opener’s context and close if their web content process terminates.

  • New Features

    • Per‑panel WKProcessPool with shared-profile WKWebsiteDataStore.
    • After termination, rebuilds WKWebView on the same pool, clears loading/progress, and shows a Reload overlay; smarter restore URL (includes in‑flight navigations).
    • Reload first calls recoverTerminatedWebContent; recovery is cleared on navigation, profile switch, and workspace reset.
    • Tests cover isolation, recovery (including empty new tabs), and popup cleanup.
    • Added Khmer translation for "Reload".
  • Refactors

    • configureWebViewConfiguration takes optional processPool, preserving an existing pool and removing a redundant overload; avoids default-argument warnings.
    • Clears browser focus mode after WKWebView replacement to prevent stuck focus.

Written for commit 652a87a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Prominent recovery overlay with a Reload button; toolbar Reload will attempt recovery first.
    • Improved per-panel isolation for web content, increasing stability between panels.
  • Bug Fixes

    • Popups now close cleanly when their web content process terminates.
    • Recovery flow preserves navigation/state and is cleared on workspace/context resets.
  • Localization

    • Added Khmer translations.
  • Tests

    • New tests covering web content termination and recovery behavior.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 2, 2026 10:31pm
cmux-staging Building Building Preview, Comment Jun 2, 2026 10:31pm

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

BrowserPanel now creates and uses a per-instance WKProcessPool, adds state and a public API for manual recovery after WKWebView web-content termination, surfaces UI to trigger recovery, closes popups on their web-content termination, and adds tests plus project wiring for these behaviors.

Changes

Per-panel process isolation and termination recovery

Layer / File(s) Summary
Core process pool isolation in BrowserPanel
Sources/Panels/BrowserPanel.swift
BrowserPanel introduces a per-instance WKProcessPool, updates makeWebView/configureWebViewConfiguration to accept an explicit processPool, creates the pool in the panel initializer, and passes it into all WebView creation/replacement paths (memory discard, profile switches, workspace reset).
Manual web-content termination recovery
Sources/Panels/BrowserPanel.swift
Adds recovery state (hasRecoverableWebContentTermination, pendingWebContentRecoveryURL), computes deferred recovery targets in replaceWebViewPreservingState, stores pending recovery when waiting for manual recovery, exposes recoverTerminatedWebContent(reason:) and clearWebContentTerminationRecovery(), clears pending recovery on normal navigation, and integrates recovery attempt into reload() and workspace-reset logic.
View: reload interception & localization
Sources/Panels/BrowserPanelView.swift, Resources/Localizable.xcstrings
Toolbar reload is intercepted to attempt deferred recovery and return early on success; a webContentRecoveryOverlay with a Reload button triggers manual recovery when panel.hasRecoverableWebContentTermination is true; Khmer localization for the Reload string added.
Popup web content process termination handling
Sources/Panels/BrowserPopupWindowController.swift
BrowserPopupWindowController adds a termination handler that closes floating popups when their WKWebView's web-content process terminates; PopupNavigationDelegate forwards webViewWebContentProcessDidTerminate(_:).
Tests and Xcode project
cmuxTests/BrowserWebContentProcessTests.swift, cmux.xcodeproj/project.pbxproj
Adds serialized @MainActor test suite covering process-pool isolation, configuration, termination replacement, reload recovery, workspace-reset interaction, and popup termination handling; updates project to compile the new test file into cmuxTests.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BrowserPanel
  participant WKWebView
  User->>BrowserPanel: normal navigation / actions
  BrowserPanel->>WKWebView: create WebView(processPool: panel.processPool)
  WKWebView->>BrowserPanel: webContentProcessDidTerminate
  BrowserPanel->>BrowserPanel: compute/store pendingWebContentRecoveryURL, set hasRecoverableWebContentTermination
  User->>BrowserPanel: recoverTerminatedWebContent(reason)
  BrowserPanel->>WKWebView: recreate WebView (using panel.processPool) and navigate to recovery URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#4243: Overlaps edits around BrowserPanel.swift WebView replacement/restore and termination handling.
  • manaflow-ai/cmux#4244: Related changes to reload() and WebView discard/restore flows that this PR augments with termination recovery.
  • manaflow-ai/cmux#4284: Touches BrowserPanelView toolbar reload behavior in the same UI path as this PR's reload interception.

"I'm a rabbit in a tiny lab,
I button-hop to mend the tab—
Each panel keeps its own small pool,
I press reload to fix the spool,
Back online with a happy nibble. 🐇"


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Architecture Rethink ❌ Error Introduces mutable state flags creating split ownership. Missing clearBrowserFocusMode in WebView replacement violates pattern in similar code paths. reload() patched as recovery workaround. Add clearBrowserFocusMode to replaceWebViewPreservingState; eliminate manual recovery side channel by making navigation automatic after WebView replacement.
Docstring Coverage ⚠️ Warning Docstring coverage is 2.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: isolating WebKit process pools for each browser panel, which is the primary architectural change across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed All new properties are isolated within @MainActor classes; no shared mutable Sendable types added; tests properly marked @MainActor.
Cmux Swift Blocking Runtime ✅ Passed No new blocking or timing-based synchronization introduced. WebContent recovery methods use synchronous, non-blocking calls without semaphores, waits, sleeps, asyncAfter, or locks.
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift, Xcode config, and localization changes. The rule covers TypeScript, JavaScript, shell, and non-Swift runtime scripts only; Swift is explicitly out of scope.
Cmux Algorithmic Complexity ✅ Passed All production code changes follow algorithmic complexity rules: hot paths (reload, recovery overlay) are O(1), no nested collection scans, profile imports use bounded collections.
Cmux Swift Concurrency ✅ Passed PR adds zero flagged async patterns: no DispatchQueue.global, no new @Published, no completion handlers, no fire-and-forget Tasks. Changes are process pool isolation and recovery logic only.
Cmux Swift @Concurrent ✅ Passed All new/modified Swift functions are synchronous and properly isolated within @MainActor-marked BrowserPanel; no async functions, invalid @concurrent annotations, or concurrent violations detected.
Cmux Swift File And Package Boundaries ✅ Passed Files are existing oversized but PR adds modest code (<100 lines net each) focused on WebContent termination recovery, below the 250-line threshold for failures on oversized files.
Cmux Swift Logging ✅ Passed All new logging uses approved cmuxDebugLog with #if DEBUG guards; no unguarded print/NSLog/debugPrint in production code; no secrets exposed.
Cmux User-Facing Error Privacy ✅ Passed User-facing changes only include generic "Reload" action button labels in recovery overlay; no vendor names, process pool details, implementation specifics, or raw error messages exposed to users.
Cmux Full Internationalization ✅ Passed User-facing text "browser.error.reload" uses String(localized:) with matching translated entries for all 20 supported locales in Localizable.xcstrings.
Cmux Swiftui State Layout ✅ Passed PR uses custom willSet/objectWillChange on existing ObservableObject (allowed legacy pattern). No new @Published/@observable, no render-time mutations, proper event handlers only.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed BrowserPopupPanel has identifier "cmux.browser-popup" registered in cmuxAuxiliaryWindowIdentifiers with proper close-shortcut handling. Lint script passes.
Description check ✅ Passed Pull request description covers all major template sections including summary of changes, testing methodology with specific commands and verification steps, and includes a comprehensive checklist with items marked complete.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-webview-crash-isolation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR isolates each browser panel into its own WKProcessPool so a WebContent crash only affects that one tab, adds a manual recovery overlay with a Reload button after crashes, and closes popup windows when their WebContent process terminates. All existing replaceWebViewPreservingState call-sites are updated to pass the per-panel pool, and configureWebViewConfiguration is changed from always injecting a shared pool to an opt-in pool parameter.

  • Per-panel WKProcessPool: Each BrowserPanel creates and holds a private let processPool, passed into makeWebView and every subsequent replacement; popups inherit the opener's pool via popupBrowserContext.processPool to stay in the same browsing context.
  • Recovery overlay: After a WebContent termination, replaceWebViewPreservingState sets hasRecoverableWebContentTermination = true and stores the restore URL; calling recoverTerminatedWebContent() (from toolbar, overlay, or panel.reload()) clears the state and resumes navigation. Profile switch and workspace-context reset both clear the recovery state.
  • Popup cleanup: PopupNavigationDelegate now implements webViewWebContentProcessDidTerminate and calls closePopup() on the controller.

Confidence Score: 4/5

Safe to merge with one open localization gap already flagged in a prior review comment: the new recovery overlay's tooltip uses browser.reload, which is still missing its Khmer translation.

The process-pool isolation, manual recovery flow, popup cleanup, and profile/workspace reset paths are all correctly implemented and covered by the new test suite. The only outstanding gap is the browser.reload tooltip missing a km entry for the new recovery overlay — this was flagged in a previous review cycle and has not been addressed in this commit.

Resources/Localizable.xcstrings — browser.reload is missing the km entry that was added to browser.error.reload in this PR.

Important Files Changed

Filename Overview
Sources/Panels/BrowserPanel.swift Core change: per-panel WKProcessPool, hasRecoverableWebContentTermination state flag, recoverTerminatedWebContent recovery path, and clearBrowserFocusMode fix on view replacement. Logic is well-structured; the open browser.reload km i18n gap remains unresolved.
Sources/Panels/BrowserPanelView.swift Adds webContentRecoveryOverlay using browser.error.reload (has km) and browser.reload tooltip (missing km); toolbar reload also short-circuits through recoverTerminatedWebContent. Overlay rendering and dismiss logic are correct.
Sources/Panels/BrowserPopupWindowController.swift Adds webViewWebContentProcessDidTerminate delegate callback that calls closePopup(); uses browserContext.processPool for configureWebViewConfiguration. Minimal, targeted, and correct.
cmuxTests/BrowserWebContentProcessTests.swift New test suite covers pool isolation, processPool preservation when omitted, webView replacement identity, reload recovery, workspace/profile reset clearing, empty-tab no-recovery, and popup termination cleanup.
Resources/Localizable.xcstrings Adds km (Khmer) to browser.error.reload (button label on recovery overlay). The sibling key browser.reload used as the safeHelp tooltip on the same overlay is still missing its km entry — flagged in a prior review comment.
cmux.xcodeproj/project.pbxproj Adds BrowserWebContentProcessTests.swift to both the file references group and the test target Sources build phase. Mechanical project file change; no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[WebContent Process Terminates] --> B[replaceWebViewAfterContentProcessTermination]
    B --> C[replaceWebViewPreservingState\nwaitForManualRecovery: true]
    C --> D{wasRenderable &&\nhasRecoveryTarget?}
    D -- No --> E[clearWebContentTerminationRecovery\nrefreshNavigationAvailability]
    D -- Yes --> F[pendingWebContentRecoveryURL = restoreURL\nhasRecoverableWebContentTermination = true]
    F --> G[Show Reload Overlay]
    G --> H{User action}
    H -- Overlay / Toolbar / panel.reload --> I[recoverTerminatedWebContent]
    H -- Navigate to new URL --> J[navigateWithoutInsecureHTTPPrompt\nclearWebContentTerminationRecovery]
    H -- Profile Switch --> K[clearWebContentTerminationRecovery]
    H -- Workspace Reset --> L[resetForWorkspaceContextChange\nclearWebContentTerminationRecovery]
    I --> M[clearWebContentTerminationRecovery\nDismiss overlay]
    M --> N{recoveryURL?}
    N -- Yes --> O[navigateWithoutInsecureHTTPPrompt to recoveryURL]
    N -- No --> P[refreshNavigationAvailability]
    subgraph Process Isolation
        Q[BrowserPanel init] --> R[new WKProcessPool per panel]
        R --> S[makeWebView with per-panel pool]
        S --> T[Popup inherits opener pool via popupBrowserContext]
    end
Loading

Reviews (14): Last reviewed commit: "Clear web content recovery on profile sw..." | Re-trigger Greptile

@lawrencecchen lawrencecchen force-pushed the task-webview-crash-isolation branch from fddf122 to 99dfbd4 Compare May 29, 2026 10:20
Comment thread Sources/Panels/BrowserPanel.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes May 29, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Panels/BrowserPanel.swift`:
- Around line 4921-4922: The manual-recovery gate currently uses only
waitForManualRecovery && wasRenderable, which can mark an about:blank/URL-less
view as recoverable; update the shouldShowManualRecovery condition to also
verify there is a real navigation target from sessionNavigationHistorySnapshot()
(e.g., check history has a current entry/url or hasEntries/canNavigate flag)
before presenting recovery UI, and apply the same check in the other occurrence
(around lines referenced 4976-4984) so recoverTerminatedWebContent() only runs
when an actual recoverable URL exists.
- Around line 4914-4918: When building restoreURL in BrowserPanel.swift, prefer
the attempted URL if a provisional main-frame navigation is active: check
navigationDelegate?.isMainFrameProvisionalNavigationActive and, when true, place
navigationDelegate?.lastAttemptedURL (and its remoteProxyDisplayURL) before
currentURL in the fallback chain used to compute restoreURL; otherwise keep the
existing ordering. Update the restoreURL expression that calls
Self.remoteProxyDisplayURL(for:), currentURL,
navigationDelegate?.lastAttemptedURL, and resolvedCurrentSessionHistoryURL() to
branch on isMainFrameProvisionalNavigationActive and swap the attempted-URL
entries into the first fallback positions.

In `@Sources/Panels/BrowserPanelView.swift`:
- Around line 1661-1681: The recovery overlay's Label uses the localized key
"browser.error.reload" (seen in webContentRecoveryOverlay) but the Khmer locale
is missing in Resources/Localizable.xcstrings; add a "km" entry for
"browser.error.reload" with the correct Khmer translation (matching the format
used for other locales), ensure the string encoding/quoting matches existing
.xcstrings entries and include the plural/variant if applicable so the Label
shows Khmer instead of falling back to English.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 94a99af9-8c0f-45cf-9315-27954b49ca52

📥 Commits

Reviewing files that changed from the base of the PR and between fddf122 and 99dfbd4.

📒 Files selected for processing (6)
  • Sources/Panels/BrowserPanel.swift
  • Sources/Panels/BrowserPanelView.swift
  • Sources/Panels/BrowserPopupWindowController.swift
  • cmuxTests/BrowserConfigTests.swift
  • cmuxTests/BrowserPanelTests.swift
  • cmuxTests/GhosttyConfigTests.swift

Comment thread Sources/Panels/BrowserPanel.swift Outdated
Comment thread Sources/Panels/BrowserPanel.swift Outdated
Comment thread Sources/Panels/BrowserPanelView.swift
Comment thread Sources/Panels/BrowserPanel.swift Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Sources/Panels/BrowserPanel.swift Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 7 files

Re-trigger cubic

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Sources/Panels/BrowserPanel.swift Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/BrowserWebContentProcessTests.swift`:
- Around line 29-32: Tests use a real network URL ("https://example.com") when
creating BrowserPanel instances which can introduce flakiness; update the
BrowserPanel initialURL arguments in the referenced spots (the BrowserPanel
initializers at the occurrences around the BrowserPanel creation and the other
two occurrences) to use a deterministic in-process URL such as "about:blank" or
a local file:// fixture instead of an external HTTPS URL so the recovery tests
don't depend on outbound networking or navigation timing.
- Around line 99-111: Add a positive precondition that the popup is actually
presented before simulating process termination: after creating popupWebView via
panel.createFloatingPopup and obtaining popupWindow, assert that
popupWindow.isVisible (or another positive signal such as popupWebView.window !=
nil or popupWebView.superview != nil) is true, then call
popupWebView.navigationDelegate?.webViewWebContentProcessDidTerminate?(popupWebView)
and finally assert the popup closed; update the test around createFloatingPopup,
popupWebView, popupWindow and the termination invocation to include this initial
visibility check so the final `#expect`(!popupWindow.isVisible) cannot pass
spuriously.

In `@Sources/Panels/BrowserPanel.swift`:
- Around line 2951-2953: The shared static fallbackConfigurationProcessPool is
overwriting a copied WKWebViewConfiguration's existing processPool when callers
omit processPool; change logic so we only assign
fallbackConfigurationProcessPool to configurations that have a nil processPool
and only when creating configurations for utility callers (e.g., in BrowserPanel
methods that use fallbackConfigurationProcessPool). Locate uses of
fallbackConfigurationProcessPool and the code path that mutates
WKWebViewConfiguration.processPool; update it to check configuration.processPool
== nil before assigning, preserving any pre-existing processPool on copied
configurations (also apply same check in the other occurrences around the lines
mentioned).
- Around line 3109-3110: The UI binding is stale because
hasRecoverableWebContentTermination is not observable; mark it as `@Published`
(e.g. change "private(set) var hasRecoverableWebContentTermination = false" to
"`@Published` private(set) var hasRecoverableWebContentTermination = false") so
BrowserPanelView receives updates; if the view also reads
pendingWebContentRecoveryURL consider making that property `@Published` as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d1fbbb35-b320-408f-ac53-ad62f740995a

📥 Commits

Reviewing files that changed from the base of the PR and between 5346b20 and 246578f.

📒 Files selected for processing (3)
  • Sources/Panels/BrowserPanel.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/BrowserWebContentProcessTests.swift

Comment thread cmuxTests/BrowserWebContentProcessTests.swift
Comment thread cmuxTests/BrowserWebContentProcessTests.swift
Comment thread Sources/Panels/BrowserPanel.swift Outdated
Comment thread Sources/Panels/BrowserPanel.swift Outdated
Comment thread Sources/Panels/BrowserPanel.swift Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Sources/Panels/BrowserPanel.swift (2)

5807-5825: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear recovery state before queueing a deferred remote navigation.

performNavigation() clears hasRecoverableWebContentTermination, but the usesRemoteWorkspaceProxy && remoteProxyEndpoint == nil path returns before it runs. After a crash on a remote workspace, typing a new URL while the proxy is disconnected leaves the stale recovery overlay armed until reconnect instead of treating the new navigation as the replacement action.

Suggested fix
     private func navigateWithoutInsecureHTTPPrompt(
         request: URLRequest,
         recordTypedNavigation: Bool,
         preserveRestoredSessionHistory: Bool = false
     ) {
         guard let url = request.url else { return }
         cancelHiddenWebViewDiscard()
         clearWebViewDiscardState(reason: "navigation")
+        clearWebContentTerminationRecovery()
         if usesRemoteWorkspaceProxy, remoteProxyEndpoint == nil {
             pendingRemoteNavigation = PendingRemoteNavigation(
                 request: request,
                 recordTypedNavigation: recordTypedNavigation,
                 preserveRestoredSessionHistory: preserveRestoredSessionHistory
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/BrowserPanel.swift` around lines 5807 - 5825,
navigateWithoutInsecureHTTPPrompt returns early when usesRemoteWorkspaceProxy &&
remoteProxyEndpoint == nil, but it doesn't clear the recoverable-crash state;
replicate what performNavigation does by clearing
hasRecoverableWebContentTermination (or invoking the same helper used there)
before queuing pendingRemoteNavigation so typing a new URL disarms the stale
recovery overlay; update the block in navigateWithoutInsecureHTTPPrompt (around
pendingRemoteNavigation and
hiddenWebViewDiscardManager.updateRestoredSessionRenderIntent) to clear that
recovery flag.

5193-5200: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Publish the chosen recovery target before arming manual recovery.

When a provisional main-frame load crashes, restoreURL can point at the attempted page while currentURL still points at the last committed page. In this branch you save restoreURL into pendingWebContentRecoveryURL but leave currentURL unchanged, so the omnibar and any session snapshot taken before the user clicks Reload still describe the wrong page.

Suggested fix
         if shouldShowManualRecovery, let restoreURL {
+            currentURL = restoreURL
             pendingWebContentRecoveryURL = restoreURL
             hasRecoverableWebContentTermination = true
             refreshNavigationAvailability()
         } else {

Also applies to: 5255-5258

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/BrowserPanel.swift` around lines 5193 - 5200, The chosen
recovery target (restoreURL) must be published before arming manual recovery so
the omnibar and session snapshot reflect the recovery page; move the assignment
that publishes the recovery target (set pendingWebContentRecoveryURL or call the
method that publishes it) to occur immediately after computing
restoreURL/restoreURLString and before setting waitForManualRecovery/arming
manual recovery (the branch that computes
shouldShowManualRecovery/shouldRestoreURL), and do the same change for the
repeated block around the 5255-5258 region so the UI/current session is updated
(e.g., currentURL/omnibar state or session snapshot publish) before you enable
manual recovery.
♻️ Duplicate comments (1)
cmuxTests/BrowserWebContentProcessTests.swift (1)

128-128: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Incomplete: Check visibility, not just window attachment.

Line 128 verifies the window is attached (popupWebView.window === popupWindow), but doesn't prove the popup was actually visible before termination. The previous review comment specifically requested a visibility check to ensure the test can't pass spuriously.

Add #expect(popupWindow.isVisible) here to confirm the popup is open before simulating termination at Line 130.

🔍 Proposed fix to add visibility precondition
 let popupWindow = try `#require`(popupWebView.window)
 `#expect`(popupWebView.window === popupWindow)
+#expect(popupWindow.isVisible)

 popupWebView.navigationDelegate?.webViewWebContentProcessDidTerminate?(popupWebView)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmuxTests/BrowserWebContentProcessTests.swift` at line 128, The test only
asserts the popup view is attached to the window (popupWebView.window ===
popupWindow) but not that the popup was visible; update the test to assert
visibility by adding a precondition check using popupWindow.isVisible (e.g., add
`#expect`(popupWindow.isVisible) immediately after the existing
popupWebView.window assertion) so the popup is confirmed visible before the
termination simulation, keeping the existing attachment assertion intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Sources/Panels/BrowserPanel.swift`:
- Around line 5807-5825: navigateWithoutInsecureHTTPPrompt returns early when
usesRemoteWorkspaceProxy && remoteProxyEndpoint == nil, but it doesn't clear the
recoverable-crash state; replicate what performNavigation does by clearing
hasRecoverableWebContentTermination (or invoking the same helper used there)
before queuing pendingRemoteNavigation so typing a new URL disarms the stale
recovery overlay; update the block in navigateWithoutInsecureHTTPPrompt (around
pendingRemoteNavigation and
hiddenWebViewDiscardManager.updateRestoredSessionRenderIntent) to clear that
recovery flag.
- Around line 5193-5200: The chosen recovery target (restoreURL) must be
published before arming manual recovery so the omnibar and session snapshot
reflect the recovery page; move the assignment that publishes the recovery
target (set pendingWebContentRecoveryURL or call the method that publishes it)
to occur immediately after computing restoreURL/restoreURLString and before
setting waitForManualRecovery/arming manual recovery (the branch that computes
shouldShowManualRecovery/shouldRestoreURL), and do the same change for the
repeated block around the 5255-5258 region so the UI/current session is updated
(e.g., currentURL/omnibar state or session snapshot publish) before you enable
manual recovery.

---

Duplicate comments:
In `@cmuxTests/BrowserWebContentProcessTests.swift`:
- Line 128: The test only asserts the popup view is attached to the window
(popupWebView.window === popupWindow) but not that the popup was visible; update
the test to assert visibility by adding a precondition check using
popupWindow.isVisible (e.g., add `#expect`(popupWindow.isVisible) immediately
after the existing popupWebView.window assertion) so the popup is confirmed
visible before the termination simulation, keeping the existing attachment
assertion intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1d2e17a9-d344-44be-bf28-5c73662ca743

📥 Commits

Reviewing files that changed from the base of the PR and between 246578f and 382e00b.

📒 Files selected for processing (5)
  • Resources/Localizable.xcstrings
  • Sources/Panels/BrowserPanel.swift
  • Sources/Panels/BrowserPanelView.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/BrowserWebContentProcessTests.swift

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Sources/Panels/BrowserPanel.swift
Comment thread cmuxTests/BrowserWebContentProcessTests.swift Outdated
@lawrencecchen
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Panels/BrowserPanel.swift`:
- Around line 5261-5269: When replacing the WKWebView during the restore/swap
path, ensure you clear the browser focus flags by calling
clearBrowserFocusMode(reason: ...) as part of the swap flow; locate the
swap/replace branch that runs navigateWithoutInsecureHTTPPrompt(...) (the path
handling restoreURL or the else refreshNavigationAvailability branch) and insert
a call to clearBrowserFocusMode(reason: <appropriate existing enum case used for
discard/context reset, e.g. .webContentProcessTerminated or .discarded>) before
rebinding the new WKWebView so isBrowserFocusModeActive /
isBrowserFocusModeExitArmed are reset immediately after the old view is
destroyed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 37c1f017-e28c-454e-8ad9-568f1c557397

📥 Commits

Reviewing files that changed from the base of the PR and between 246578f and df888ac.

📒 Files selected for processing (5)
  • Resources/Localizable.xcstrings
  • Sources/Panels/BrowserPanel.swift
  • Sources/Panels/BrowserPanelView.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/BrowserWebContentProcessTests.swift

Comment thread Sources/Panels/BrowserPanel.swift
@lawrencecchen lawrencecchen dismissed stale reviews from coderabbitai[bot], coderabbitai[bot], and coderabbitai[bot] June 2, 2026 14:24

Dismissed stale CodeRabbit changes-requested review after all inline comments were addressed or resolved and the latest PR checks passed.

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Jun 2, 2026

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7802ef3. Configure here.

Comment thread Sources/Panels/BrowserPanel.swift
@lawrencecchen lawrencecchen force-pushed the task-webview-crash-isolation branch from f3a1d89 to 652a87a Compare June 2, 2026 22:27
@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 2, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@lawrencecchen lawrencecchen merged commit 5e1c6d3 into main Jun 2, 2026
20 checks passed
@lawrencecchen lawrencecchen deleted the task-webview-crash-isolation branch June 2, 2026 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant